package exebuilder.core;

import exebuilder.FileUtil;
import exebuilder.command.CommandUtils;
import exebuilder.controller.EndController;
import exebuilder.controller.MainController;
import exebuilder.data.AppConfig;
import exebuilder.ui.MessageBox;
import javafx.application.Platform;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.DecimalFormat;
import java.util.ArrayList;

public class AppBuilder {

    public static final String APP_NAME = "MyApplication";

    private static final Logger logger = LogManager.getLogger();

    String moduleDeps;

    String runtimePath;

    AppConfig appConfig;

    String appTempDir;

    String appRuntimePath;

    String outputDir;


    MainController mainController;
    EndController endController;

    public AppBuilder() {
        initData();
    }

    private void initData() {
        appConfig = AppConfig.getInstance();
        if (StringUtils.isBlank(appConfig.getAppName())) {
            appConfig.setAppName(APP_NAME);
        }
        this.mainController = (MainController) Controllers.get("main");
        this.endController = (EndController) Controllers.get("end");
        this.appTempDir = appConfig.getTempDir() + appConfig.getAppName() + "_unpack";
        this.appRuntimePath = appTempDir + appConfig.getAppName() + "\\app\\runtime";
        this.outputDir = appConfig.getOutputPath();
    }

    public void build() {
        try {
            mainController.backButton.setDisable(true);
            mainController.nextButton.setDisable(true);
            endController.buildInfoArea.clear();
            DecimalFormat format = new DecimalFormat("0.0");
            double startTime = System.currentTimeMillis();
            logger.debug("开始检测运行环境");
            initJavaEnv();
            logger.debug("初始化临时目录");
            initTempDir();
            logger.debug("开始分析运行依赖");
            analyzeDependencies();
            logger.debug("开始创建运行时");
            createRuntime();
            logger.debug("开始应用程序镜像");
            createAppImage();
            logger.debug("清除临时文件");
            deleteRuntimeDir();
            copyFolder();
            logger.info("执行完毕");
            double endTime = System.currentTimeMillis();
            var times = (endTime - startTime) / 1_000;
            Platform.runLater(() -> endController.buildInfoArea.appendText("执行完毕，耗时:" + format.format(times) + "s\n"));
            AppConfig.getInstance().saveData();

        } catch (Exception e) {
            logger.error("command error:", e);
            Platform.runLater(() -> MessageBox.error(e.getMessage()));
        } finally {
            mainController.backButton.setDisable(false);
            mainController.nextButton.setDisable(false);
        }

    }

    private void initJavaEnv() throws Exception {
        int jdkVersion = CommandUtils.getJdkVersion(appConfig.getJdkPath());
        if (jdkVersion >= 17) {
            Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("java version:%s\n", jdkVersion)));
            logger.debug("java version:" + jdkVersion);
        } else {
            Platform.runLater(() -> MessageBox.error("JDK版本必须不低于“17”"));
            throw new Exception("JDK版本必须不低于“17”");
        }
    }

    private void initTempDir() throws Exception {
        if (Files.isWritable(Paths.get(appConfig.getTempDir()))) {
            if (Files.exists(Paths.get(appTempDir))) {
                FileUtil.deleteDir(appTempDir);
            }
        } else {
            throw new Exception("系统临时目录读写异常");
        }
        logger.debug("app临时目录:" + appTempDir);
    }

    private void analyzeDependencies() throws Exception {
        var mainJarPath = appConfig.getSourcePath() + "\\" + appConfig.getMainJarFile();
        var libPath = appConfig.getLibPath();
        ArrayList<String> commandLine = new ArrayList<>();
        commandLine.add("--multi-release");
        commandLine.add(appConfig.getJdkVersion().toString());
        commandLine.add("-q");
        commandLine.add("--print-module-deps");
        commandLine.add("--ignore-missing-deps");
        commandLine.add(mainJarPath);
        if (!StringUtils.isBlank(libPath)) {
            commandLine.add(libPath + "\\*.jar");
        }
//  if (appConfig.getAppType() == 2) {
//   Platform.runLater(() -> endController.buildInfoArea.appendText("解析SpringBoot模块...\n"));
//   analyzeSpringBootJar();
//   commandLine.add( + "\\springboot_temp\\BOOT-INF\\lib\\*.jar");
//  }
        moduleDeps = CommandUtils.execute(new File(appConfig.getJdkPath(), "bin"), "jdeps", commandLine).trim();
        Platform.runLater(() -> endController.buildInfoArea.appendText(String.format("查找依赖模块:%s\n", moduleDeps)));
        logger.debug("moduleDeps:{}", moduleDeps);
    }

    private void createRuntime() throws Exception {
        var runtimePath = appTempDir + "\\runtime";
        var runtimeDir = Paths.get(runtimePath);
        if (Files.exists(runtimeDir)) {
            FileUtil.deleteDir(runtimePath);
        }
        ArrayList<String> commandLine = new ArrayList<>();
        commandLine.add("--add-modules");
        commandLine.add(this.moduleDeps);
        commandLine.add("--no-header-files");
        commandLine.add("--no-man-pages");
        commandLine.add("--compress");
        commandLine.add("2");
        commandLine.add("--output");
        commandLine.add(runtimePath);
        String out = "";
        try {
            out = CommandUtils.execute(new File(appConfig.getJdkPath(), "bin"), "jlink", commandLine);
            this.runtimePath = runtimePath;
            logger.info("运行时创建成功");
            Platform.runLater(() -> endController.buildInfoArea.appendText("运行时创建成功\n"));
        } catch (Exception e) {
            logger.error("运行时创建失败:{}", out);
            String finalOut = out;
            Platform.runLater(() -> MessageBox.error("运行时创建失败:" + finalOut));
        }
    }

    private void createAppImage() throws Exception {
        ArrayList<String> commandLine = new ArrayList<>();
        commandLine.add("-t");
        commandLine.add("app-image");
        commandLine.add("-i");
        commandLine.add(appConfig.getSourcePath());
        commandLine.add("-n");
        commandLine.add(appConfig.getAppName());
        commandLine.add("--main-jar");
        commandLine.add(".\\" + appConfig.getMainJarFile());
        commandLine.add("--main-class");
        commandLine.add(appConfig.getMainClass());
        commandLine.add("--runtime-image");
        commandLine.add(runtimePath);
        if (!StringUtils.isBlank(appConfig.getVersion())) {
            commandLine.add("--app-version");
            commandLine.add(appConfig.getVersion());
        }
        if (!StringUtils.isBlank(appConfig.getIconPath())) {
            commandLine.add("--icon");
            commandLine.add(appConfig.getIconPath());
        }
        if (!StringUtils.isBlank(appConfig.getCopyright())) {
            commandLine.add("--copyright");
            commandLine.add(appConfig.getCopyright());
        }
        commandLine.add("--dest");
        commandLine.add(appTempDir);
        if (appConfig.getAppType() == 1 || appConfig.getAppType() == 2) {
            commandLine.add("--win-console");
        }
        var out = CommandUtils.execute(new File(appConfig.getJdkPath(), "bin"), "jpackage", commandLine);
        if (out.isBlank()) {
            logger.info("镜像文件创建成功");
            Platform.runLater(() -> endController.buildInfoArea.appendText("镜像文件创建成功\n"));
        } else {
            logger.error("运行时镜像创建失败:{}", out);
            Platform.runLater(() -> MessageBox.error("镜像文件创建失败：" + out));
        }
    }

    private void copyFolder() throws IOException {
        var sourcePath = new File(appTempDir);
        var destPath = new File(outputDir);
        var appDestPath = new File(outputDir + "\\" + appConfig.getAppName());
        if (appDestPath.exists()) {
            FileUtils.deleteDirectory(appDestPath);
        }
        FileUtils.copyDirectory(sourcePath, destPath);
        FileUtil.deleteDir(appTempDir);
        logger.debug("delete appTempDir {}", appTempDir);
    }

// private void analyzeSpringBootJar() {
//  Path path = Paths.get(, "springboot_temp");
//  try {
//   if (!Files.exists(path)) {
//    Files.createDirectory(path);
//    logger.debug("start unzip springboot jar file");
//    ZipHelper.unzip(appConfig.getSourcePath() + "\\" + appConfig.getMainJarFile(), path.toString());
//    logger.debug("unzip sucess");
//   }
//  } catch (IOException e) {
//   e.printStackTrace();
//  }
// }

    private void deleteRuntimeDir() throws IOException {
        var runtimeDir = Paths.get(runtimePath);
        var appRuntimeDir = Paths.get(appRuntimePath);
        if (Files.exists(runtimeDir)) {
            FileUtil.deleteDir(runtimePath);
            logger.debug("delete runtime dir {}", runtimePath);
        }
        if (Files.exists(appRuntimeDir)) {
            FileUtil.deleteDir(appRuntimePath);
            logger.debug("delete appRuntime dir {}", appRuntimeDir);
        }
    }
}
